#! /bin/sh
#
# Copyright 2006-2010 Red Hat, Inc. and/or its affiliates.
#
# Licensed to you under the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.  See the files README and
# LICENSE_GPL_v2 which accompany this distribution.
#

# chkconfig: 2345 99 00
#
### BEGIN INIT INFO
# Provides: vdsmd
# Required-Start: $syslog $network
# Should-Start: $time
# Required-Stop: $syslog
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Description: init script for the VDS management server
# Short-Description: init script for the VDS management server
### END INIT INFO

. @LIBEXECDIR@/ovirt_functions.sh

VDSM_BIN=@VDSMDIR@/vdsm
CONF_FILE=@CONFDIR@/vdsm.conf
GETCONFITEM=@VDSMDIR@/get-conf-item
prog=vdsm
PIDFILE=@VDSMRUNDIR@/vdsmd.pid
RESPAWNPIDFILE=@VDSMRUNDIR@/respawn.pid
CORE_DUMP_PATH=/var/log/core/core.%p.%t.dump
DOM_METADATA_BACKUP_DIR=/var/log/vdsm/backup
CORE_PATTERN=/proc/sys/kernel/core_pattern
QEMU_DUMP_PATH="/var/log/core"
NEEDED_SERVICES="iscsid multipathd ntpd wdmd sanlock network"
CONFLICTING_SERVICES="libvirt-guests"

# trigger for reconfiguration
FORCE_RECONFIGURE=@VDSMLIBDIR@/reconfigure

LCONF=/etc/libvirt/libvirtd.conf
QCONF=/etc/libvirt/qemu.conf
LDCONF=/etc/sysconfig/libvirtd
QLCONF=/etc/libvirt/qemu-sanlock.conf

is_coredump=`$GETCONFITEM $CONF_FILE vars core_dump_enable false | tr A-Z a-z`
[ $is_coredump != true ] && is_coredump=false

SYSTEMCTL_SKIP_REDIRECT=true

. /etc/init.d/functions

log_failure_msg() { echo -n "$@"; failure "$@"; echo; }
log_success_msg() { echo -n "$@"; success "$@"; echo; }

mk_data_center() {
    local dc
    dc=`$GETCONFITEM $CONF_FILE irs repository /rhev/`
    /bin/mkdir -p "$dc"
    /bin/chown vdsm.kvm "$dc"
}

mk_dom_backup() {
    /bin/mkdir -p ${DOM_METADATA_BACKUP_DIR} > /dev/null 2>&1
    /bin/chown vdsm.kvm `dirname ${DOM_METADATA_BACKUP_DIR}` > /dev/null 2>&1
    /bin/chown vdsm.kvm ${DOM_METADATA_BACKUP_DIR} > /dev/null 2>&1
}

mk_core_path() {
    core_path=/var/log/core
    if ! [ -d $core_path ]; then
        /bin/mkdir -p $core_path > /dev/null 2>&1
    fi
    /bin/chmod a+tw $core_path > /dev/null 2>&1
}

mk_run_path() {
    local run_path

    for run_path in "@VDSMRUNDIR@" "@POOLSDIR@"; do
        /bin/mkdir -p "$run_path"
        /bin/chmod 755 "$run_path"
        /bin/chown vdsm:kvm "$run_path"
        /sbin/restorecon "$run_path"
    done
}

get_libvirt_conf_item() {
    local cfile key

    cfile=$1
    key=$2
    /bin/grep "^\s*$key\s*=" "$cfile" | \
            /usr/bin/tail -1 | /bin/sed "s/\s*$key\s*=\s*//;s/\s*\(#.*\)\?$//"
}

test_conflicting_conf() {
    local listen_tcp auth_tcp ssl

    ssl=`$GETCONFITEM $CONF_FILE vars ssl true | tr A-Z a-z`
    [ "$ssl" == true ] && return 0

    listen_tcp="`get_libvirt_conf_item $LCONF listen_tcp`"
    auth_tcp="`get_libvirt_conf_item $LCONF auth_tcp`"
    spice_tls="`get_libvirt_conf_item $QCONF spice_tls`"

    if [ "$listen_tcp" == 1 -a "$auth_tcp" == '"none"' -a "$spice_tls" == 0 ];
    then
        return 0
    else
        echo "conflicting vdsm and libvirt-qemu tls configuration."
        echo "vdsm.conf with ssl=False requires the following changed: "
        echo "libvirtd.conf: listen_tcp=1, auth_tcp=\"none\", "
        echo "qemu.conf: spice_tls=0."
        return 1
    fi
}

shutdown_conflicting_srv() {
    local srv

    for srv in $CONFLICTING_SERVICES
    do
        /sbin/chkconfig $srv off
        if /sbin/service $srv status > /dev/null 2>&1;
        then
            if [ "$srv" == "libvirt-guests" ]; then
                /bin/rm -f /var/lock/subsys/libvirt-guests
            else
                /sbin/service $srv stop
            fi
        fi
    done
    return 0
}

libvirt_should_use_upstart() {
    [[ -x /sbin/initctl ]]
}

start_needed_srv() {
    local srv
    local ret_val

    for srv in $NEEDED_SERVICES
    do
        if ! /sbin/service $srv status > /dev/null 2>&1;
        then
            echo "Starting $srv..."
            /sbin/service $srv start
            ret_val=$?
            if [ $ret_val -ne 0 ]
            then
                log_failure_msg "$prog: Dependent $srv failed to start"
                return $ret_val
            fi
        fi
    done

    /sbin/service iscsid force-start
}

test_lo() {
    if ! LC_ALL=C /sbin/ip link show lo | /bin/grep -q UP;
    then
        log_failure_msg "VDSMD: lo interface is down, can't run !"
        echo "VDSMD: lo interface is down, can't run !" > /dev/kmsg
        return 1
    fi
    return 0
}

free_space() {
    local path=$1
    df -P "$path" | awk '{print $4}'| tail -1
}

test_space() {
    local MIN_SPACE_KB=10000

    if [ "`free_space /var/log/vdsm`" -lt "$MIN_SPACE_KB" ]; then
        log_failure_msg "$prog: low log space"
        return 1
    fi
    return 0
}


test_already_running()
{
    if pidofproc -p $RESPAWNPIDFILE >/dev/null || \
       pidofproc -p $PIDFILE $VDSM_BIN >/dev/null; then
        log_success_msg "$prog: already running"
        return 0
    fi
    return 1
}

# configure libvirt to vdsm's needs
configure_libvirt()
{
    local force_reconfigure="$1"
    local by_vdsm="by vdsm"
    # The PACKAGE_VERSION macro is not used here because we do not want to
    # update the libvirt configure file every time we change vdsm package
    # version. In fact the configure generated here is almost unrelated to the
    # package version, so anything meaningful can be used here. Since a hard
    # coded version string has been already used, for compatibility we will
    # continue to use this string.
    local by_vdsm_vers="4.9.11"
    local start_conf_section="## beginning of configuration section ${by_vdsm}"
    local end_conf_section="## end of configuration section ${by_vdsm}"
    local ts=/etc/pki/vdsm

    # Remove configuration created by vdsm (old "# by vdsm" and the new format)
    # Argument: configuration file that will be inspected
    remove_vdsm_conf() {
        sed -i --copy -e "/${start_conf_section}/,/${end_conf_section}/d" \
            -e "/$by_vdsm/d" "$@"
    }

    set_if_default() {
        local cfile key val
        cfile="$1"
        key="$2"
        val="$3"

        /bin/grep -q "^\s*$key\s*=" "$cfile" || \
          echo "$key=$val" >> "$cfile"
    }

    if isOvirt
    then
        . /usr/libexec/ovirt-functions
    else
        ovirt_store_config() { :; }
    fi

    local lconf qconf ldconf
    local ssl=`$GETCONFITEM $CONF_FILE vars ssl true | tr A-Z a-z`

    lconf="$2"
    qconf="$3"
    ldconf="$4"
    qlconf="$5"

    # do not configure ovirt nodes before registration
    if isOvirt
    then
        if [ ! -f /etc/pki/vdsm/certs/vdsmcert.pem ]
        then
            log_failure_msg "$prog: Missing certificates, $prog not registered"
            return 6
        fi
        /usr/bin/vdsm-tool validate-ovirt-certs
    fi

    #
    # reconfigure if:
    # - force specified or
    # - we have the FORCE_RECONFIGURE trigger file
    # - not configured
    #
    if [ "$force_reconfigure" != "force" ] &&
       ! [ -f "$FORCE_RECONFIGURE" ] &&
       grep -q "$by_vdsm_vers" $lconf && grep -q "$by_vdsm_vers" $qconf && \
       grep -q "$by_vdsm_vers" $ldconf && grep -q "$by_vdsm_vers" $qlconf
    then
        log_success_msg $"$prog: libvirt already configured for vdsm "
        return 0
    fi

    echo $"Configuring libvirt for vdsm..."

    # Remove a previous configuration (if present)
    remove_vdsm_conf $lconf $qconf $ldconf $qlconf

    # Write to all conf files the *initial* message of vdsm changes
    for arg in "${lconf}" "${qconf}" "${ldconf}" "${qlconf}"
    do
        echo "${start_conf_section}-${by_vdsm_vers}" >> "${arg}"
    done

    # Set the default values for libvirt and qemu
    set_if_default $lconf listen_addr \"0.0.0.0\"
    set_if_default $lconf unix_sock_group \"kvm\"
    set_if_default $lconf unix_sock_rw_perms \"0770\"
    set_if_default $lconf auth_unix_rw \"sasl\"
    set_if_default $lconf host_uuid \"$(uuidgen)\"
    set_if_default $qconf dynamic_ownership 0

    if [[ "$ssl" == true ]]; then
        set_if_default $qconf spice_tls 1
    else
        set_if_default $qconf spice_tls 0
    fi
    set_if_default $ldconf LIBVIRTD_ARGS --listen
    set_if_default $ldconf DAEMON_COREFILE_LIMIT unlimited
    set_if_default $qconf save_image_format \"lzop\"
    # FIXME until we are confident with libvirt integration, let us have a verbose log
    set_if_default $lconf log_outputs \"1:file:/var/log/libvirtd.log\"
    set_if_default $lconf log_filters "\"1:libvirt 3:event 3:json 1:util 1:qemu\""

    # If the ssl flag is set, update the libvirt and qemu configuration files
    # with the location for certificates and permissions.
    if [ -f $ts/certs/cacert.pem -a \
         -f $ts/certs/vdsmcert.pem -a \
         -f $ts/keys/vdsmkey.pem -a \
         "$ssl" == true ];
    then
        set_if_default $lconf ca_file \"$ts/certs/cacert.pem\"
        set_if_default $lconf cert_file \"$ts/certs/vdsmcert.pem\"
        set_if_default $lconf key_file \"$ts/keys/vdsmkey.pem\"
        set_if_default $qconf spice_tls_x509_cert_dir \"$ts/libvirt-spice\"
    else
        set_if_default $lconf auth_tcp \"none\"
        set_if_default $lconf listen_tcp 1
        set_if_default $lconf listen_tls 0
    fi

    # Configuring sanlock
    set_if_default $qconf lock_manager \"sanlock\"
    set_if_default $qlconf auto_disk_leases 0
    set_if_default $qlconf require_lease_for_disks 0

    # Configuring auto dump path
    set_if_default $qconf auto_dump_path \"$QEMU_DUMP_PATH\"

    # Write to all conf files the *end* message of vdsm changes
    for arg in "${lconf}" "${qconf}" "${ldconf}" "${qlconf}"
    do
        echo "${end_conf_section}-${by_vdsm_vers}" >> "${arg}"
    done

    local lnetwork=/etc/libvirt/qemu/networks/autostart/default.xml
    rm -f $lnetwork

    local llogr=/etc/logrotate.d/libvirtd
    local stanza=`mktemp`
    /bin/cat > "$stanza" <<EOF
# vdsm
/var/log/libvirtd.log {
    rotate 100
    missingok
    copytruncate
    size 15M
    compress
    compresscmd /usr/bin/xz
    uncompresscmd /usr/bin/unxz
    compressext .xz
}
# end vdsm
EOF
    /bin/sed -e "/# vdsm/,/# end vdsm/d" "$llogr" >> "$stanza"
    local oldmod=`/usr/bin/stat --format=%a "$llogr"`
    /bin/mv "$stanza" "$llogr"
    if [ -n "$oldmod" ]; then
       /bin/chmod "$oldmod" "$llogr"
    fi
    restorecon "$llogr"

    ovirt_store_config "$lconf" "$qconf" "$ldconf" "$llogr"

    /sbin/initctl restart libvirtd 2>/dev/null || :

    #
    # finished reconfiguration, do not trigger
    # next time
    #
    if [ -f "$FORCE_RECONFIGURE" ]; then
        rm -f "$FORCE_RECONFIGURE"
    fi
}


RETVAL=0

reconfigure() {
    local args="$@"
    case $# in
        0)
            args="force $LCONF $QCONF $LDCONF $QLCONF"
            ;;
        1)
            # Use default paths
            args="$args $LCONF $QCONF $LDCONF $QLCONF"
            ;;
    esac
    configure_libvirt $args
}

stop_libvirtd_sysv() {
    # stop libvirt SysV service if we intend to configure upstart

    if libvirt_should_use_upstart && ! [[ -f /etc/init/libvirtd.conf ]]; then
        /sbin/chkconfig libvirtd off
        /sbin/service libvirtd stop
    fi
}

start_libvirtd() {
    local packaged target
    local startout

    if ! libvirt_should_use_upstart; then
        /sbin/service libvirtd start
        return
    fi

    packaged=`/bin/rpm -ql libvirt libvirt-daemon | \
              /bin/grep libvirtd.upstart | /usr/bin/tail -1`
    target=/etc/init/libvirtd.conf

    if [[ -f "$packaged" ]] && ! diff -q "$packaged" "$target" >/dev/null;
    then
        /bin/cp -p "$packaged" "$target" || return 1
        /sbin/initctl reload-configuration
    fi

    startout=`/sbin/initctl start libvirtd 2>&1`
    if [[ "$?" -eq 0 || "$startout" =~ .*already\ running.* ]];
    then
        return 0
    else
        echo "$startout" >&2
        return 1
    fi
}

start() {
    local ret_val
    python @VDSMDIR@/hooks.pyc before_vdsm_start

    shutdown_conflicting_srv && stop_libvirtd_sysv

    if ! @LIBEXECDIR@/vdsm-gencerts.sh --check; then
        echo -n $"Configuring a self-signed VDSM host certificate: "
        @LIBEXECDIR@/vdsm-gencerts.sh && success || failure ; echo
    fi

    reconfigure noforce
    ret_val=$?
    if [ $ret_val -ne 0 ]
    then
        log_failure_msg "$prog: failed to reconfigure libvirt"
        return $ret_val
    fi

    start_needed_srv && start_libvirtd
    ret_val=$?
    if [ $ret_val -ne 0 ]
    then
       log_failure_msg "$prog: one of the dependent services did not start, error code $ret_val"
       return $ret_val
    fi

    python @VDSMDIR@/nwfilter.pyc > /dev/null 2>&1
    ret_val=$?
    if [ $ret_val -ne 0 ]
    then
        log_failure_msg "$prog: Failed to define network filters on libvirt"
        return $ret_val
    fi

    python @VDSMDIR@/dummybr.pyc
    ret_val=$?
    if [ $ret_val -ne 0 ]
    then
        log_failure_msg "$prog: Failed to create ephemeral dummy bridge."
        return $ret_val
    fi

    @VDSMDIR@/vdsm-restore-net-config
    /usr/bin/vdsm-tool load-needed-modules
    mk_data_center
    mk_core_path
    mk_dom_backup
    mk_run_path
    /bin/chmod 1777 /dev/shm
    if [ $is_coredump == true ]; then
        export DAEMON_COREFILE_LIMIT=unlimited
        echo $CORE_DUMP_PATH > $CORE_PATTERN
    fi

    test_already_running && return 0

    if ! (test_space && test_lo && \
          test_conflicting_conf); then
        return 1
    fi

    echo $"Starting up vdsm daemon: "
    local vdsm_nice=`$GETCONFITEM $CONF_FILE vars vdsm_nice -5`

    LIBVIRT_LOG_FILTERS=`$GETCONFITEM $CONF_FILE vars libvirt_log_filters "1:libvirt 1:remote"` \
    LIBVIRT_LOG_OUTPUTS=`$GETCONFITEM $CONF_FILE vars libvirt_log_outputs "1:file:/var/log/vdsm/libvirt.log"` \
    LC_ALL=C NICELEVEL=$vdsm_nice daemon --user=vdsm @VDSMDIR@/respawn --minlifetime 10 --daemon --masterpid $RESPAWNPIDFILE $VDSM_BIN
    RETVAL=$?
    [ "$RETVAL" -eq 0 ] && log_success_msg $"$prog start" || log_failure_msg $"$prog start"
    [ "$RETVAL" -eq 0 ] && touch /var/lock/subsys/vdsmd
}

stop() {
    echo $"Shutting down vdsm daemon: "
    if killproc -p $RESPAWNPIDFILE; then
        log_success_msg $"$prog watchdog stop"
    fi
    if ! pidofproc -p $PIDFILE >/dev/null; then
        log_failure_msg "$prog: not running"
        RETVAL=0
    else
        killproc -p $PIDFILE -d 2
        RETVAL=$?
        [ "$RETVAL" -eq 0 ] && log_success_msg $"$prog stop" || log_failure_msg $"$prog stop"
        [ "$RETVAL" -eq 0 ] && rm -f /var/lock/subsys/vdsmd
    fi
    python @VDSMDIR@/hooks.pyc after_vdsm_stop
    return $RETVAL
}

case "$1" in
     start)
        start
	RETVAL=$?
	;;
     stop)
        stop
	RETVAL=$?
	;;
     status)
	pidofproc -p $PIDFILE $VDSM_BIN >/dev/null
	RETVAL=$?
	if [ "$RETVAL" -eq 0 ]; then
	    echo "VDS daemon server is running"
	else
	    echo -n "VDS daemon is not running"
            if pidofproc -p $RESPAWNPIDFILE >/dev/null; then
                echo ", but its watchdog is"
            else
                echo
            fi
	fi
	;;
     condrestart)
	pidofproc -p $PIDFILE $VDSM_BIN >/dev/null
	RETVAL=$?
	if [ "$RETVAL" -eq 0 ]; then
	    $0 stop && $0 start;
	    RETVAL=$?;
	fi;
        ;;
     try-restart)
	$0 stop && $0 start
	RETVAL=$?
	;;
     restart|force-reload)
	$0 stop
	$0 start
	RETVAL=$?
	;;
    reconfigure)
        # Jump over 'reconfigure'
        shift 1
        reconfigure "$@"
	RETVAL=$?
    ;;
     *)
	echo "Usage: $0 {start|stop|status|restart|force-reload|try-restart}"
	RETVAL=2
esac

exit $RETVAL
